package com.aplink.generic.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import android.os.Environment;

public class SimpleCrypto {

    private static String encryptedFileName = "sellproof.ck";
    private final static String HEX = "0123456789ABCDEF";

    private static String seedUserFrequency = "APLINK";

    public static boolean activeUserLog(final String userId) {

        boolean isSuccessful = false;

        StringBuilder clearContent = new StringBuilder();
        clearContent = SimpleCrypto.getRawDataFromEncryptedFile();

        if (clearContent == null) {
            return false;
        }

        // we append new data in raw file
        final Calendar currentCal = Calendar.getInstance();
        final int newyear = currentCal.get(Calendar.YEAR);
        final int newmonth = currentCal.get(Calendar.MONTH) + 1;
        final int newday = currentCal.get(Calendar.DAY_OF_MONTH);
        final int newhourofday = currentCal.get(Calendar.HOUR_OF_DAY);
        final int newminute = currentCal.get(Calendar.MINUTE);
        final int newmilisecond = currentCal.get(Calendar.SECOND);

        // finally we encrypt it again and write back into the file
        final String newRecord = String.valueOf(newmonth) + "-"
                + String.valueOf(newday) + "-" + String.valueOf(newyear) + " "
                + String.valueOf(newhourofday) + ":"
                + String.valueOf(newminute) + ":"
                + String.valueOf(newmilisecond) + "/" + userId + ";";

        clearContent.append(newRecord);

        isSuccessful = SimpleCrypto.encryptRawDataIntoFile(clearContent);

        // to pull data from sdcard into ws
        return isSuccessful;

    }

    private static void appendHex(final StringBuffer sb, final byte b) {
        sb.append(SimpleCrypto.HEX.charAt((b >> 4) & 0x0f)).append(
                SimpleCrypto.HEX.charAt(b & 0x0f));
    }

    private static byte[] decrypt(final byte[] raw, final byte[] encrypted)
            throws Exception {
        final SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        final Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        return cipher.doFinal(encrypted);
    }

    public static String decrypt(final String seed, final String encrypted)
            throws Exception {
        final byte[] rawKey = SimpleCrypto.getRawKey(seed.getBytes());
        final byte[] enc = SimpleCrypto.toByte(encrypted);
        final byte[] result = SimpleCrypto.decrypt(rawKey, enc);
        return new String(result);
    }

    private static byte[] encrypt(final byte[] raw, final byte[] clear)
            throws Exception {
        final SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        final Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        return cipher.doFinal(clear);
    }

    public static String encrypt(final String seed, final String cleartext)
            throws Exception {
        final byte[] rawKey = SimpleCrypto.getRawKey(seed.getBytes());
        final byte[] result = SimpleCrypto
                .encrypt(rawKey, cleartext.getBytes());
        return SimpleCrypto.toHex(result);
    }

    private static boolean encryptRawDataIntoFile(final StringBuilder rawData) {
        boolean isSuccessful = false;

        if (rawData == null) {
            return false;
        }

        try {
            if (!Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                // handle case of no SDCARD present
            } else {
                final File encryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator
                                + "SellProof" // folder name
                                + File.separator
                                + SimpleCrypto.encryptedFileName); // file name

                /***
                 * we must create the new file in this folder in any case
                 */

                if (!encryptedFile.exists()) {
                    encryptedFile.createNewFile(); // create the file
                } else {
                    encryptedFile.delete();
                    encryptedFile.createNewFile(); // create the file
                }

                final FileOutputStream fos = new FileOutputStream(encryptedFile);

                // firstly we encrypt the data
                /***
                 * We must write encrypt content and save in theFile
                 */
                // getRawKey from seed
                final byte[] rawKey = SimpleCrypto
                        .getRawKey(SimpleCrypto.seedUserFrequency.getBytes());
                final SecretKeySpec sks = new SecretKeySpec(rawKey, "AES");
                // create cipher
                final Cipher cipher = Cipher
                        .getInstance("AES/CBC/PKCS5Padding");
                final IvParameterSpec ivParameterSpec = new IvParameterSpec(
                        rawKey);
                cipher.init(Cipher.ENCRYPT_MODE, sks, ivParameterSpec);

                // create the inputstream of clearContent
                // convert String into InputStream
                final InputStream is = new ByteArrayInputStream(rawData
                        .toString().getBytes());

                // Wrap the output stream
                final CipherOutputStream cos = new CipherOutputStream(fos,
                        cipher);
                // Write bytes
                int b;
                final byte[] d = new byte[8];
                while ((b = is.read(d)) != -1) {
                    cos.write(d, 0, b);
                }
                cos.flush();

                if (cos != null) {
                    cos.close();
                }
                if (fos != null) {
                    fos.close();
                }
                if (is != null) {
                    is.close();
                }

            }

        } catch (final Exception e) {
            e.printStackTrace();
            isSuccessful = false;
        }

        return isSuccessful;
    }

    public static String fromHex(final String hex) {
        return new String(SimpleCrypto.toByte(hex));
    }

    public static StringBuilder getRawDataFromEncryptedFile() {

        StringBuilder clearContent = new StringBuilder();
        try {
            if (!Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                // handle case of no SDCARD present
            } else {
                final File encryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator
                                + "SellProof" // folder name
                                + File.separator
                                + SimpleCrypto.encryptedFileName); // file name

                // In the first time, the encrypted file doesn't exist

                /***
                 * we must create the file in this folder
                 */

                if (!encryptedFile.exists()) {
                    encryptedFile.getParentFile().mkdir();
                    encryptedFile.createNewFile(); // create the file
                    return clearContent;
                }

                final Calendar cal = Calendar.getInstance(); // this code is to
                                                             // create
                // an unique file name
                final String idString = String.valueOf(cal.getTimeInMillis());

                final File decryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator + "SellProof" // folder name
                                + File.separator + idString + ".ck"); // file
                // name
                // we create the new file
                decryptedFile.createNewFile(); // create the file

                /***
                 * Eventually, we must read the encrypted content and decrypt
                 * them into clear text and save in decryptedFile
                 */
                // getRawKey from seed
                final byte[] rawKey = SimpleCrypto
                        .getRawKey(SimpleCrypto.seedUserFrequency.getBytes());
                final SecretKeySpec sks = new SecretKeySpec(rawKey, "AES");
                // create cipher
                final Cipher cipher = Cipher
                        .getInstance("AES/CBC/PKCS5Padding");
                final IvParameterSpec ivParameterSpec = new IvParameterSpec(
                        rawKey);
                cipher.init(Cipher.DECRYPT_MODE, sks, ivParameterSpec);

                final FileInputStream fis = new FileInputStream(encryptedFile);

                final FileOutputStream fos = new FileOutputStream(decryptedFile);

                // decrypt the file
                final CipherInputStream cis = new CipherInputStream(fis, cipher);
                int b;
                final byte[] d = new byte[8];
                while ((b = cis.read(d)) != -1) {
                    // write it into the output
                    fos.write(d, 0, b);
                }

                fos.flush();
                fos.close();
                cis.close();
                fis.close();

                // we read a gain the clear file and then delete the file
                final FileInputStream fcis = new FileInputStream(decryptedFile);
                final BufferedReader buf = new BufferedReader(
                        new InputStreamReader(fcis));
                String readString = new String();
                // just reading each line
                while ((readString = buf.readLine()) != null) {
                    clearContent.append(readString);
                    System.out.print("<<<<<<<<<< Clear data:"
                            + clearContent.toString());
                }

                buf.close();
                fcis.close();
                decryptedFile.delete(); // delete the file

                // finally return the String of clear data
            }

        } catch (final Exception e) {
            e.printStackTrace();
            clearContent = null;
        }

        return clearContent;

    }

    private static byte[] getRawKey(final byte[] seed) throws Exception {
        final KeyGenerator kgen = KeyGenerator.getInstance("AES");
        final SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        sr.setSeed(seed);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        final SecretKey skey = kgen.generateKey();
        final byte[] raw = skey.getEncoded();
        return raw;
    }

    public static ArrayList<String> getUserLog(final String contactId) {

        final ArrayList<String> finalResult = new ArrayList<String>();

        // we firstly and fetch into the String builder to get raw data
        StringBuilder clearContent = new StringBuilder("");
        clearContent = SimpleCrypto.getRawDataFromEncryptedFile();

        if ((clearContent != null)
                && (clearContent.toString().compareTo("") != 0)) {

            final String[] subUsers = clearContent.toString().split(";");
            // secondly, we check with contactId
            for (final String subUser : subUsers) {
                if (subUser.contains(contactId) == true) {
                    finalResult.add(subUser);
                }
            }
        }

        // delete the file
        final File encryptedFile = new File(
                Environment.getExternalStorageDirectory() + File.separator
                        + "SellProof" // folder name
                        + File.separator + SimpleCrypto.encryptedFileName); // file
                                                                            // name

        if (encryptedFile.exists()) {
            encryptedFile.delete();
        }

        return finalResult;
    }

    public static byte[] toByte(final String hexString) {
        final int len = hexString.length() / 2;
        final byte[] result = new byte[len];
        for (int i = 0; i < len; i++) {
            result[i] = Integer.valueOf(
                    hexString.substring(2 * i, (2 * i) + 2), 16).byteValue();
        }
        return result;
    }

    public static String toHex(final byte[] buf) {
        if (buf == null) {
            return "";
        }
        final StringBuffer result = new StringBuffer(2 * buf.length);
        for (final byte element : buf) {
            SimpleCrypto.appendHex(result, element);
        }
        return result.toString();
    }

    public static String toHex(final String txt) {
        return SimpleCrypto.toHex(txt.getBytes());
    }

}
